package com.binance.basedao.config;

import com.binance.basedao.mybatis.ParseTableNameAspect;
import com.binance.basedao.mybatis.SqlInterceptor;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.type.JdbcType;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;

import javax.sql.DataSource;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Properties;
import java.util.logging.Logger;

/**
 * Description: Base dao config
 *
 * @author hongchaoMao - Date 2018/12/5
 */
@Configuration
public class BaseDaoConfig {

    private final Logger logger = Logger.getLogger(getClass().getName());

    private static final String DEFAULT_MAPPER_LOCATION = "classpath*:com/binance/*/data/**/*.xml";

    /**
     * mapper xml扫描路径以mybatis-spring中mybatis.mapper-locations配置值优先
     */
    @Value("${mybatis.mapper-locations:null}")
    private String[] mapperLocations;

    @Bean
    @Primary
    public SqlSessionFactory sqlSessionFactory(@Qualifier("dataSource") DataSource dataSource) throws Exception {
        SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
        sessionFactory.setDataSource(dataSource);
        sessionFactory.setMapperLocations(this.getMapperLocation());
        sessionFactory.setConfiguration(this.getConfiguration());
        // 采用basedao中的分页sql拦截器,否则BaseService中的分页方法不可用
        SqlInterceptor page = new SqlInterceptor();
        sessionFactory.setPlugins(new Interceptor[]{page});
        return sessionFactory.getObject();
    }

    private Resource[] getMapperLocation() {
        ResourcePatternResolver resourceResolver = new PathMatchingResourcePatternResolver();
        List<Resource> resources = new ArrayList<>();
        if (!(mapperLocations.length == 1 && "null".equals(mapperLocations[0]))) {
            for (String mapperLocation : this.mapperLocations) {
                try {
                    Resource[] mappers = resourceResolver.getResources(mapperLocation);
                    resources.addAll(Arrays.asList(mappers));
                } catch (IOException e) {
                    // ignore
                    logger.warning("------>getMapperLocation() " + e.getMessage());
                }
            }
        } else {
            try {
                Resource[] mappers = resourceResolver.getResources(DEFAULT_MAPPER_LOCATION);
                resources.addAll(Arrays.asList(mappers));
            } catch (Exception ex) {
                // ignore
                logger.warning("------>getMapperLocation() " + ex.getMessage());
            }
        }
        return resources.toArray(new Resource[0]);
    }

    private org.apache.ibatis.session.Configuration getConfiguration() {
        org.apache.ibatis.session.Configuration config = new org.apache.ibatis.session.Configuration();
        // 设置xml mapper文件中的字段映射支持自动转换为驼峰属性
        config.setMapUnderscoreToCamelCase(true);
        Properties properties = new Properties();
        properties.setProperty("dialect", "MySQL");
        config.setVariables(properties);
        // 当没有为参数提供特定的 JDBC 类型时，为空值指定 JDBC 类型,避免未指定jdbcType的空值参数而报错
        config.setJdbcTypeForNull(JdbcType.NULL);
        // 全局地开启或关闭配置文件中的所有映射器已经配置的任何缓存。(建议关闭)
        // 不同namespace或者namespace中涉及到多表查询，以及分布式环境下都容易出现脏读
        // 对于频繁查询的数据想通过缓存提高性能,强烈建议用集中式缓存实现(如redis)
        config.setCacheEnabled(false);
        // 全局启用或禁用延迟加载。当禁用时，所有关联对象都会即时加载
        config.setLazyLoadingEnabled(false);
        // 配置默认的执行器。SIMPLE 就是普通的执行器；REUSE 执行器会重用预处理语句（prepared statements）;
        // BATCH 执行器将重用语句并执行批量更新。
        config.setDefaultExecutorType(ExecutorType.SIMPLE);
        return config;
    }

    @Bean
    public ParseTableNameAspect parseTableNameAspect(){
        return new ParseTableNameAspect();
    }

}
